package com.mdlaser.api;

import cn.dev33.satoken.exception.NotLoginException;
import com.mdlaser.annotation.CommonRest;
import com.mdlaser.annotation.NormalRest;
import com.mdlaser.exception.RestException;
import com.mdlaser.util.SpringBeanUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.core.MethodParameter;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.List;
import java.util.Map;

@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler implements ResponseBodyAdvice<Object> {

    /**
     * 参数校验异常
     */
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseBody
    public ResponseResult<Object> error(Exception e) {
        e.printStackTrace();
        log.error("参数校验异常:{}", e.getMessage());
        return ResponseResult.failed(ResponseCode.ARGUMENT_VALID_ERROR.getCode(), e.getMessage(), null);
    }


    /**
     * 参数校验异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public ResponseResult<Object> error(MethodArgumentNotValidException e) {
        log.error("参数校验异常:{}", e.getMessage());
        // 获取异常体
        BindingResult bindingResult = e.getBindingResult();
        // 从异常体中获取所有异常列表
        List<FieldError> errors = bindingResult.getFieldErrors();
        StringBuilder messageBuilder = new StringBuilder();// 要返回的字符串,拼接的是参数名称以及对应的报错提示
        String message = null;
        for (FieldError error : errors) {
            String field = error.getField();// 报错的参数
            message = error.getDefaultMessage();// 报错的信息
            messageBuilder.append(field).append("\t").append(message).append("\n");// 拼接参数名称以及对应的报错提示
        }
        log.error("参数校验异常详情:{}", messageBuilder.toString());
        return ResponseResult.failed(ResponseCode.ARGUMENT_VALID_ERROR.getCode(), message, null);
    }


    /**
     * 空指针异常
     */
    @ExceptionHandler(NullPointerException.class)
    @ResponseBody
    public ResponseResult<Object> error(NullPointerException e) {
        log.error("空指针异常", e);
        return ResponseResult.failed("系统异常");
    }

    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseBody
    public ResponseResult<Object> error(MissingServletRequestParameterException e) {
        log.error("参数异常", e);
        return ResponseResult.failed("接口参数传递不全，请检查");
    }


    /**
     * 拦截运行时异常
     */
    @ExceptionHandler(value = RuntimeException.class)
    @ResponseBody
    public ResponseResult<Object> runtimeExceptionHandle(RuntimeException e) {
        log.error("运行时异常", e);
        return ResponseResult.failed(e.getMessage());
    }


    @ExceptionHandler(value = NotLoginException.class)
    @ResponseBody
    public ResponseResult<Object> NotLoginExceptionHandle(NotLoginException e) {
        log.error(e.getMessage());
        return ResponseResult.failed(ResponseCode.UNAUTHORIZED.getCode(), ResponseCode.UNAUTHORIZED.getMessage(), null);
    }

    @ExceptionHandler(value = DuplicateKeyException.class)
    @ResponseBody
    public ResponseResult<Object> duplicateKeyExceptionHandle(DuplicateKeyException e) {
        return ResponseResult.failed("数据库主键冲突");
    }


    @ExceptionHandler(value = HttpMessageNotReadableException.class)
    @ResponseBody
    public ResponseResult<Object> httpMessageNotReadableExceptionHandle(HttpMessageNotReadableException e) {
        log.error("Http请求转化异常", e);
        return ResponseResult.failed("请求内容转化异常，请检查http请求格式");
    }

    @ExceptionHandler(value = HttpMediaTypeNotAcceptableException.class)
    @ResponseBody
    public ResponseResult<Object> error(HttpMediaTypeNotAcceptableException e) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String requestURI = request.getRequestURI();
        log.error("Http媒体异常,请求路径" + requestURI);
        return ResponseResult.failed("Http媒体异常");
    }


    /**
     * 兜底异常，捕获Operation异常
     */
    @ExceptionHandler(value = Throwable.class)
    @ResponseBody
    public ResponseResult<Object> throwableHandle(Throwable e) {
        log.error("捕获系统级异常", e);
        return ResponseResult.failed("系统异常");
    }

    @ExceptionHandler(value = NoHandlerFoundException.class)
    @ResponseBody
    public ResponseResult<Object> throwableHandle(NoHandlerFoundException e) {
        return ResponseResult.failed("资源不存在");
    }


    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
    @ResponseBody
    public ResponseResult<Object> httpRequestMethodNotSupportedExceptionHandle(HttpRequestMethodNotSupportedException e) {
        return ResponseResult.failed("请求方法错误");
    }


    @ExceptionHandler(value = RestException.class)
    @ResponseBody
    public ResponseResult<Object> restExceptionHandle(Throwable th) {
        return ResponseResult.failed(th.getMessage());
    }

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        NormalRest normalRest = returnType.getExecutable().getAnnotation(NormalRest.class);
        if (normalRest != null) {
            return false;
        }
        CommonRest annotation = returnType.getContainingClass().getAnnotation(CommonRest.class);
        return annotation != null;
    }


    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (body instanceof String || body instanceof ResponseResult) {
            return body;
        }
        return ResponseResult.success(body);
    }
}
